import java.math.BigInteger;
import java.util.Random;
import java.util.Scanner;

public class ElGamal {
    static BigInteger p, g;
    static BigInteger C, C1;

    public static void getRandomP(int alpha) {
        Random r = new Random();
        BigInteger q = null;
        while (true) {
            q = BigInteger.probablePrime(alpha, r);
            if (q.bitLength() != alpha) {
                continue;
            }
            if (q.isProbablePrime(10)) {
                p = q.multiply(new BigInteger("2")).add(BigInteger.ONE);
                if (p.isProbablePrime(10)) {
                    break;
                }
            }
        }
        while (true) {
            g = BigInteger.probablePrime(p.bitLength() - 1, r);
            if (!g.modPow(BigInteger.ONE, p).equals(BigInteger.ONE)
                    && !g.modPow(q, p).equals(BigInteger.ONE)) {
                break;
            }
        }
    }

    public static BigInteger getRandoma(BigInteger p) {
        BigInteger a;
        Random r = new Random();
        a = new BigInteger(p.bitLength() - 1, r);
        return a;
    }

    public static BigInteger calculatey(BigInteger x, BigInteger g, BigInteger p) {
        BigInteger y;
        y = g.modPow(x, p);
        return y;
    }

    public static void encrypt(String m, BigInteger y, BigInteger p, BigInteger g) {
        BigInteger message = new BigInteger(m.getBytes());
        Random r = new Random();
        BigInteger k;
        while (true) {
            k = new BigInteger(p.bitLength() - 1, r);
            if (k.gcd(p.subtract(BigInteger.ONE)).equals(BigInteger.ONE)) {
                break;
            }
        }
        C = g.modPow(k, p);
        C1 = message.multiply(y.modPow(k, p)).mod(p);
    }

    public static String decrypt(BigInteger C, BigInteger C1, BigInteger a, BigInteger p) {
        BigInteger scy = C1.multiply(C.modPow(a.negate(), p)).mod(p);
        String str = new String(scy.toByteArray());
        return str;
    }

    public static void main(String[] args) {
        BigInteger y, x;
        System.out.println("请输入明文:");
        while (true) {
            Scanner input = new Scanner(System.in);
            String str = input.nextLine();
            ElGamal.getRandomP(new BigInteger(str.getBytes()).bitLength());
            x = ElGamal.getRandoma(p);
            y = ElGamal.calculatey(x, g, p);
            System.out.println("计算机随机生成的素数P:" + p);
            System.out.println("求得其生成元:" + g);
            System.out.println("私钥<x,g,p>为: (" + x + "," + g + "," + p + ")");
            System.out.println("公钥<y,g,p>为:" + "(" + y + "," + g + "," + p + ")");
            x = ElGamal.getRandoma(p);
            y = ElGamal.calculatey(x, g, p);
            ElGamal.encrypt(str, y, p, g);
            System.out.println("加密后的密文为:" + C + "," + C1);
            String designm = ElGamal.decrypt(C, C1, x, p);
            System.out.println("解密得到明文为:" + designm);
        }
    }
}